package com.demo.batch.schedule;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.demo.service.BatchJnlService;

/**
 *	场景：分批 处理文件数据 / 各批次自行将数据入库（无需汇总结果）
 * @author ytzhu
 *
 */
@Component
public class JucBatchFileTask {
	
	final int splitCount = 10 ;	// 总拆分批次
	ExecutorService executor = Executors.newFixedThreadPool(splitCount) ;	// 线程池定义
	CountDownLatch latch = new CountDownLatch(splitCount) ;	// 计数器10个，倒数到0后继续
	
	@Autowired
	private BatchJnlService BatchJnlService ;
	
	@Async	//将方法异步化
	@Scheduled(cron = "50 26 16 * * ?")
	public void batchFileProcess() {
		long startTime = System.currentTimeMillis() ;
		System.out.println("JucBatchFileTask begin....") ;
		
		// 1. 计算10个线程的起止记录行号
		long totalLineCount = 0 ; // 文件数据总行数（行号从1开始）
		File file = new File("D:\\Eclipse\\eclipse-workspace\\SpringBoot-BatchFile\\BatchTest.txt") ;
		try {
			totalLineCount = Files.lines(Paths.get(file.getPath())).count() ;
			System.out.println("totalLineCount:"+totalLineCount);
		} catch (IOException e) {
			e.printStackTrace();
		}
		int batchLineCount = Long.valueOf(totalLineCount).intValue() / splitCount ; // 每批次中的处理的数据行数（不能整除时，最后一批会比该值多一些）
		System.out.println("batchLineCount:"+batchLineCount);

		// 2.启动10个线程
		for(int i=0; i<splitCount; i++) {
			int temp = i ;
			int totalCount = Long.valueOf(totalLineCount).intValue() ;
			executor.submit(()->{
				int beginLineNum = temp * batchLineCount + 1 ; 
				int endLineNum ;
				if(temp == splitCount-1) {
					endLineNum = totalCount ; //最后一批，直接到最后一行（应对不能均分的情况）
				}else {
					endLineNum = (temp+1) * batchLineCount;
				}
				System.out.println("当前批次号："+ temp +"，处理数据行号：["+beginLineNum+","+endLineNum+"]");
				// 2.1 读取文件内容（按行号区间）
				List<String[]> dataList = this.readFile(file, beginLineNum, endLineNum) ;
				// 2.2 数据处理
				List<Map> newDataList = this.processData(dataList) ;
				// 2.3 数据入库
				Integer saveCount = this.BatchJnlService.saveBatchJnl(newDataList) ;
				System.out.println("当前批次号："+ temp +"，入库成功行数："+saveCount);
				
				latch.countDown(); // 计数器减一
			}) ;
		}

		// 3.使用CountDownLatch等待所有线程完成（计数器为0时）
		try {
			latch.await(60, TimeUnit.SECONDS) ;	// 设置超时时间（60秒）
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		// 4.输出成功日志
		System.out.println("JucBatchFileTask end! on time:" + (System.currentTimeMillis() - startTime));
	}

	/**
	 * 	读取数据文件内容
	 * @param file	数据文件对象
	 * @param beginLineNum	起始行号（包含）
	 * @param endLineNum	结束行号（包含）
	 * @return
	 */
	public List<String[]> readFile(File file, int beginLineNum, int endLineNum) {
		List<String[]> dataList = new ArrayList<String[]>() ;
		try {
			LineNumberReader lineReader = new LineNumberReader(new FileReader(file));
			String dataStr = null ;
			while((dataStr = lineReader.readLine()) != null) {
				if(lineReader.getLineNumber() >= beginLineNum && lineReader.getLineNumber() <= endLineNum) {
					String[] dataArray = dataStr.split("\\|") ;
					if(6 != dataArray.length) {
						// 格式不正确，则抛错，暂且输出日志
						System.out.println("Data Error");
						return null;
					}
					dataList.add(dataArray) ;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}  
		return dataList ;
	}
	
	/**
	 * 	处理数据（将每行数据中的金额，都乘以100）
	 * @param contentList
	 * @return	数据List，数据Item为处理后的Map对象
	 */
	private List<Map> processData(List<String[]> contentList) {
		List<Map> resultList = new ArrayList<Map>() ;
		for(String[] dataArray : contentList) {
			Map dataMap = new HashMap() ;
			dataMap.put("id", dataArray[0]) ;
			BigDecimal amount1 = new BigDecimal(dataArray[1]).multiply(new BigDecimal("100")) ;
			dataMap.put("amount1", amount1.toString()) ;
			BigDecimal amount2 = new BigDecimal(dataArray[2]).multiply(new BigDecimal("100")) ;
			dataMap.put("amount2", amount2.toString()) ;
			BigDecimal amount3 = new BigDecimal(dataArray[3]).multiply(new BigDecimal("100")) ;
			dataMap.put("amount3", amount3.toString()) ;
			BigDecimal amount4 = new BigDecimal(dataArray[4]).multiply(new BigDecimal("100")) ;
			dataMap.put("amount4", amount4.toString()) ;
			BigDecimal amount5 = new BigDecimal(dataArray[5]).multiply(new BigDecimal("100")) ;
			dataMap.put("amount5", amount5.toString()) ;
			
			resultList.add(dataMap) ;
		}
		return resultList;
	}

}
